/**
 * 
 */
package dataflowscheme.engine.core;

import java.util.HashMap;
import java.util.Map;

/**
 * Class intended to record statistics about time of processing events.
 * This class is singleton.
 * 
 * @author <a href="mailto:misiak7@gmail.com">Michal Antolik</a>
 *
 */
public class ExecutionEPstats {

	private static ExecutionEPstats instance = null;
	
	public static ExecutionEPstats getInstance(){
		if(instance == null){
			instance = new ExecutionEPstats();
		}
		return instance;
	}
	
	private ExecutionEPstats() {
	}
	
	/** time spend for all execution events = serviceTime + processingTime 
	 * of all execution events */
	private long allTime = 0;
	
	/** time spend on processing of all execution events - this time
	 * does not include service time required by engine to handle events */
	private long allProcessingTime = 0;
	
	/** time spend on processing of particular processing entity(Task or Storage)
	 * does not include service time required by engine to handle events */
	private Map<String, Long> processingTimePerPE = new HashMap<String, Long>();
	
	/**
	 * submit the statistics from execution event (TaskExecutionEvent, StorageExecutionEvent)
	 * 
	 * @param serviceTime time spend by engine to handle results
	 * @param processingTime time spend to create an instance and perform "run" method
	 * @param pe name of the PE
	 */
	synchronized public void submit(long serviceTime, long processingTime, String pe){
		allTime += serviceTime + processingTime;
		allProcessingTime += processingTime;
		
		Long oldPT;
		if(processingTimePerPE.containsKey(pe)){
			oldPT = processingTimePerPE.get(pe);
		} else {
			//initialize time
			oldPT = new Long(0);
		}
		processingTimePerPE.put(pe, oldPT + processingTime);
	}
	
	/**
	 * statistics types:
	 * (a) how much time takes initialization phase of program (steps 1-3)
	 *  and run-time phase (steps 4-5) 
	 * (b) how much processing time takes engine service - sending, receiving,
	 *  rejecting events and processing data produced by referenced classes.
	 *  All the rest time was used for creation of referenced classes and 
	 *  invoking its run methods. A service rate information can also provide an
	 *  information about grain structure of data-flow program. For instance,
	 *  a service rate of 70% means that program is too  fine-grained and in
	 *  the scheme there are many entities with too simple code in referenced
	 *  classes.
	 * (c) how much processing time of all execution event parser's threads was
	 *  used for particular processing entities (STORAGE, TASK)
	 * (d) what was the load factor of event parser - rate of time while thread
	 *  was suspended due waiting for events and while thread was busy with
	 *  engine service. When a rate reaches 100%, then it means that a program
	 *  can not run faster and the boundary was reached.
	 * 
	 * @return statistics 
	 */
	public String printStats(){
		StringBuilder sb = new StringBuilder();
		
		//all processors time, include service time spend by event parser
		long totalTime = allTime + epST; 
		
		//if some processing was done
		if((totalTime != 0) && (allProcessingTime != 0)){
			long totalServiceTime = allTime - allProcessingTime + epST;
			
			int servicePercent = (int)(totalServiceTime*100/totalTime);
			sb.append("Total service time takes " + servicePercent + "% of processors time.\n");
			
			//execution event parser statistics
			sb.append("Processing time of particular PEs:\n");
			for(String pe : processingTimePerPE.keySet()){
				int percent = (int)(processingTimePerPE.get(pe)*100/allProcessingTime);
				sb.append("  - " + pe + ": " + percent + "%\n");
			}
		}
		
		
		//event parser statistics
		int busy = (int)(epST*100/(epWT + epST));
		sb.append("Busy time of event parser: " + busy + "% (For rest time was handling thread sleeping)\n");

		return sb.toString();
	}

	/** event parser service time */
	private long epST = 0;
	
	/** event parser waiting time (thread sleeps - was blocked 
	 * when taking new event from queue) */
	private long epWT = 0;
	
	/**
	 * submit statistics from event parser from one iteration
	 * 
	 * @param serviceTime
	 * @param waitingTime
	 */
	public void submitEventParserTime(long serviceTime, long waitingTime){
		epST += serviceTime;
		epWT += waitingTime;
	}
}
